package com.ssy.lingxi.product.serviceImpl.platform;

import cn.hutool.core.bean.BeanUtil;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.model.vo.PageVO;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.product.api.model.request.CategoryAttributeRequest;
import com.ssy.lingxi.product.api.model.request.CategoryAttributeSortRequest;
import com.ssy.lingxi.product.common.enums.CommodityAttributeTypeEnum;
import com.ssy.lingxi.product.entity.platform.Attribute;
import com.ssy.lingxi.product.entity.platform.AttributeValue;
import com.ssy.lingxi.product.entity.platform.Category;
import com.ssy.lingxi.product.entity.platform.ProductCategoryAttribute;
import com.ssy.lingxi.product.repository.platform.AttributeRepository;
import com.ssy.lingxi.product.repository.platform.CategoryRepository;
import com.ssy.lingxi.product.repository.platform.ProductCategoryAttributeRepository;
import com.ssy.lingxi.product.service.platform.ICategoryAttributeService;
import com.ssy.lingxi.product.utils.PageUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.text.Collator;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 品类属性实现类
 * @author jw.chen
 * @version 2.0.0
 * @date 2020/6/22
 */
@Service
public class CategoryAttributeServiceImpl implements ICategoryAttributeService {

    @Autowired
    private CategoryRepository categoryRepository;

    @Autowired
    private AttributeRepository attributeRepository;

    @Resource
    private ProductCategoryAttributeRepository productCategoryAttributeRepository;

    /**
     * 查询分类下对应的属性
     * @param pageVO
     * @param name
     * @param categoryId
     * @param isByCategory 是否查该分类下的属性
     * @return Page<CategoryAttribute>
     */
    @Override
    public Page<Attribute> getCategoryAttributeList(PageVO pageVO, String name, String groupName, Long categoryId, Boolean isByCategory) {
        //查询是否存在该品类
        Category category = categoryRepository.findById(categoryId).orElse(null);
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize());
        Page<Attribute> result;
        if (category != null) {
            if (isByCategory) {
                result = attributeRepository.findByNameContainingAndCategoryListId(name, categoryId, page);
                supplementSort(result, categoryId);
            } else {
                List<Attribute> attributeList = category.getAttributeList();
                List<Long> idNotInList = attributeList.stream().map(Attribute::getId).collect(Collectors.toList());
                result = attributeRepository.findAll(getSpecification(name, groupName, idNotInList), page);
                supplementSort(result, categoryId);
            }
            return result;
        } else {
            throw new BusinessException(ResponseCode.PRODUCT_CATEGORY_NOT_EXIST);
        }
    }

    private void supplementSort(Page<Attribute> result, Long categoryId) {
        Map<Integer, Integer> productCategoryAttributeMap = productCategoryAttributeRepository.findAllByCategoryId(categoryId.intValue())
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(ProductCategoryAttribute::getAttributeId, ProductCategoryAttribute::getSort));
        result.getContent().forEach(attribute -> {
            attribute.setSort(productCategoryAttributeMap.get(attribute.getId().intValue()));
        });
    }

    /**
     * 查询分类下对应的属性-MRO模式 按照特定的排序返回结果
     * @param name
     * @param categoryId
     * @param isByCategory 是否查该分类下的属性
     * @return Page<CategoryAttribute>
     */
    @Override
    public List<Attribute> getMroCategoryAttributeList(String name, String groupName, Long categoryId, Boolean isByCategory) {
        //查询是否存在该品类
        Category category = categoryRepository.findById(categoryId).orElse(null);
        if (category != null) {
            List<Attribute> sortedAttributeList;
//            List<Long> subCategoryIdList = getSubCategoryIdList(category.getId());
            if (isByCategory) {
                sortedAttributeList = sortResult(attributeRepository.findByNameContainingAndCategoryListIdIn(name, new ArrayList<Long>(){{add(categoryId);}}), categoryId, Boolean.TRUE);
            } else {
                List<Attribute> attributeList = category.getAttributeList();
                List<Long> idNotInList = attributeList.stream().map(Attribute::getId).collect(Collectors.toList());
                sortedAttributeList = sortResult(attributeRepository.findAll(getSpecification(name, groupName, idNotInList)), categoryId, Boolean.TRUE);
            }
            return sortedAttributeList;
        } else {
            throw new BusinessException(ResponseCode.PRODUCT_CATEGORY_NOT_EXIST);
        }
    }


    /**
     * 给结果排序
     *
     * @param attributeList
     * @param categoryId
     * @return
     */
    private List<Attribute> sortResult(List<Attribute> attributeList, Long categoryId) {
        List<ProductCategoryAttribute> productCategoryAttributeList = productCategoryAttributeRepository.findAllByCategoryId(categoryId.intValue());
        Map<Integer, Integer> map = productCategoryAttributeList.stream().collect(Collectors.toMap(ProductCategoryAttribute::getAttributeId, ProductCategoryAttribute::getSort));
        return attributeList
                .stream()
                .peek(attribute -> attribute.setSort(Objects.nonNull(map.get(attribute.getId().intValue())) ? map.get(attribute.getId().intValue()) : 0))
                .sorted(Comparator.comparing(Attribute::getSort)
                        .thenComparing(Attribute::getId, Comparator.reverseOrder()))
                .collect(Collectors.toList());
    }

    /**
     * 给结果排序 MRO模式排序
     *
     * @param attributeList
     * @param categoryId
     * @param openMro       是否开启mro
     * @return
     */
    private List<Attribute> sortResult(List<Attribute> attributeList, Long categoryId, Boolean openMro) {
        //查询品类属性中间表
        List<ProductCategoryAttribute> productCategoryAttributeList = productCategoryAttributeRepository.findAllByCategoryId(categoryId.intValue());

        //过滤无效的属性
        attributeList = filterInvalidAttribute(attributeList);

        //过滤无效的属性值
        filterInvalidAttributeValue(attributeList);

        //将中间表数据转成map
        Map<Integer, Integer> map = turnListToMap(productCategoryAttributeList);

        //开启MRO之后，属性按照sort排序，然后按照id倒叙排序，属性值按照自定义的比较器排序
        return attributeList
                .stream()
                .filter(this::distinct)
                .peek(attribute -> setAttributeSort(map, attribute))
                .sorted(Comparator.comparing(Attribute::getSort)
                        .thenComparing(Attribute::getId, Comparator.reverseOrder()))
                .peek(attribute -> {
                    if (openMro) {
                        sortAttributeValue(attribute);
                    }
                })
                .collect(Collectors.toList());
    }

    /**
     * 给属性值排序
     *
     * @param attribute
     */
    private void sortAttributeValue(Attribute attribute) {
        Comparator<Object> comparator = Collator.getInstance(Locale.CHINESE);
        attribute.setAttributeValueList(attribute.getAttributeValueList()
                .stream()
                .sorted((p1, p2) -> comparator.compare(p1.getValue(), p2.getValue()))
                .collect(Collectors.toList()));
    }

    /**
     * 设置属性排序
     *
     * @param map
     * @param attribute
     */
    private void setAttributeSort(Map<Integer, Integer> map, Attribute attribute) {
        attribute.setSort(Objects.nonNull(map.get(attribute.getId().intValue())) ? map.get(attribute.getId().intValue()) : 0);
    }

    /**
     * 给属性去重
     *
     * @param attribute
     * @return
     */
    private boolean distinct(Attribute attribute) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return seen.putIfAbsent(attribute.getId(), Boolean.TRUE) == null;
    }

    /**
     * 将中间表数据转成map
     *
     * @param productCategoryAttributeList
     * @return
     */
    private Map<Integer, Integer> turnListToMap(List<ProductCategoryAttribute> productCategoryAttributeList) {
        return productCategoryAttributeList
                .stream()
                .collect(Collectors.toMap(ProductCategoryAttribute::getAttributeId, ProductCategoryAttribute::getSort));
    }

    /**
     * 过滤无效的属性值
     *
     * @param attributeList
     */
    private void filterInvalidAttributeValue(List<Attribute> attributeList) {
        attributeList.forEach(attribute -> {
            List<AttributeValue> attributeValueList = attribute.getAttributeValueList();
            attributeValueList = attributeValueList.stream()
                    .filter(AttributeValue::getIsEnable)
                    .collect(Collectors.toList());
            attribute.setAttributeValueList(attributeValueList);
        });
    }

    /**
     * 过滤无效的属性
     *
     * @param attributeList
     * @return
     */
    private List<Attribute> filterInvalidAttribute(List<Attribute> attributeList) {
        attributeList = attributeList
                .stream()
                .filter(Attribute::getIsEnable)
                .filter(item -> !CommodityAttributeTypeEnum.Input.getCode().equals(item.getType()))
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() ->
                                new TreeSet<>(Comparator.comparing(Attribute::getId))), ArrayList::new));
        return attributeList;
    }


    /**
     * 添加/修改品类
     * @param CategoryAttributeRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveCategoryAttribute(CategoryAttributeRequest CategoryAttributeRequest){
        //获取参数值
        Long categoryId = CategoryAttributeRequest.getCategoryId();
        //查询是否存在该品类
        Category category = categoryRepository.findById(categoryId).orElse(null);
        if(category == null){
            throw new BusinessException(ResponseCode.PRODUCT_CATEGORY_NOT_EXIST);
        }

        //查询是否存在该属性列表
        List<Long> attributeIdList = CategoryAttributeRequest.getAttributeIds();
        List<Attribute> attributeList = attributeRepository.findAllById(attributeIdList);
        if(attributeList.isEmpty() || attributeList.size() != attributeIdList.size()){
            throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_NOT_EXIST);
        }

        List<ProductCategoryAttribute> oldCategoryAttributeList = findProductCategoryAttributeData(categoryId);

        //合并之前的属性
        List<Attribute> oldAttributeList = category.getAttributeList();
        if(oldAttributeList != null && !oldAttributeList.isEmpty()){
            attributeList.addAll(oldAttributeList);
        }
        //设置对应关系
        category.setAttributeList(attributeList);
        //数据库持久化对象
        categoryRepository.saveAndFlush(category);

        List<ProductCategoryAttribute> newCategoryAttributeList = findProductCategoryAttributeData(categoryId);

        resetSort(oldCategoryAttributeList, newCategoryAttributeList);

        return ResponseCode.SUCCESS.getMessage();
    }

    /**
     * 重新设置sort
     * 方法主要做的事情是匹配两个List中categoryId和attributeId相等的对象
     * 如果存在，则用旧的对象sort给新的对象赋值
     * 如果不存在，则不做处理
     * 可适用于新增，修改，删除品类属性后reset中间表的sort字段
     *
     * @param oldAttributeSort
     * @param newAttributeSort
     */
    public void resetSort(List<ProductCategoryAttribute> oldAttributeSort, List<ProductCategoryAttribute> newAttributeSort) {
        List<ProductCategoryAttribute> productCategoryAttributeList = newAttributeSort
                .stream()
                .map(newData -> oldAttributeSort
                        .stream()
                        .map(oldData -> {
                            if (attributeEquals(oldData, newData)) {
                                newData.setSort(oldData.getSort());
                            }
                            return newData;
                        })
                        .collect(Collectors.toList())
                )
                .flatMap(List::stream)
                .collect(Collectors
                        .collectingAndThen(
                                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCategoryId() + ";" + o.getAttributeId()))), ArrayList::new));
        productCategoryAttributeList.forEach(item->{
            productCategoryAttributeRepository.updateData(item.getCategoryId(),item.getAttributeId(),item.getSort());
        });
    }

    /**
     * 比较两份数据的attributeId是否相等
     *
     * @param newData
     * @param oldData
     * @return
     */
    private boolean attributeEquals(ProductCategoryAttribute newData, ProductCategoryAttribute oldData) {
        return oldData.getAttributeId().equals(newData.getAttributeId());
    }

    /**
     * 根据categoryId查询中间表数据
     * @param customerCategoryId
     * @return
     */
    private List<ProductCategoryAttribute> findProductCategoryAttributeData(Long customerCategoryId) {
        return productCategoryAttributeRepository.findAllByCategoryId(customerCategoryId.intValue());
    }

    /**
     * 删除品类
     * @param categoryAttributeRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String deleteCategoryAttribute(CategoryAttributeRequest categoryAttributeRequest) {
        //获取参数值
        List<Long> attributeIdList = categoryAttributeRequest.getAttributeIds();
        Long categoryId = categoryAttributeRequest.getCategoryId();
        //查询是否存在该属性列表
        List<Attribute> attributeList = attributeRepository.findAllById(attributeIdList);
        if(attributeList.isEmpty() || attributeList.size() != attributeIdList.size()){
            throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_NOT_EXIST);
        }
        //查询是否存在该品类
        Category category = categoryRepository.findById(categoryId).orElse(null);
        if(category == null){
            throw new BusinessException(ResponseCode.PRODUCT_CATEGORY_NOT_EXIST);
        }
        //判断是否存在关联关系
        List<Attribute> currentList = category.getAttributeList();
        if(currentList == null || currentList.isEmpty() || !currentList.containsAll(attributeList)){
            throw new BusinessException(ResponseCode.PRODUCT_ATTRIBUTE_NOT_EXIST);
        }
        List<ProductCategoryAttribute> oldCategoryAttributeData = findProductCategoryAttributeData(categoryId);

        //删除绑定关系
        category.getAttributeList().removeAll(attributeList);
        categoryRepository.saveAndFlush(category);

        List<ProductCategoryAttribute> newCategoryAttributeData = findProductCategoryAttributeData(categoryId);

        resetSort(oldCategoryAttributeData, newCategoryAttributeData);
        return ResponseCode.SUCCESS.getMessage();
    }

    /**
     * 构建查询条件
     */
    private Specification<Attribute> getSpecification(String name, String groupName, List<Long> idNotInList){
        return (Specification<Attribute>) (root, query, criteriaBuilder) -> {
            Predicate finalConditions = criteriaBuilder.conjunction();

            //id筛选
            if(idNotInList != null && !idNotInList.isEmpty()){
                finalConditions = criteriaBuilder.and(finalConditions, criteriaBuilder.not(criteriaBuilder.in(root.get("id")).value(idNotInList)));
            }

            //属性名称
            if(!StringUtils.isEmpty(name)){
                Predicate predicate = criteriaBuilder.like(root.get("name").as(String.class), "%" + name + "%");
                finalConditions = criteriaBuilder.and(finalConditions, predicate);
            }

            //属性组名称
            if(!StringUtils.isEmpty(groupName)){
                Predicate predicate = criteriaBuilder.like(root.get("groupName").as(String.class), "%" + groupName + "%");
                finalConditions = criteriaBuilder.and(finalConditions, predicate);
            }

            return  query.where(finalConditions).getRestriction();
        };
    }

    /**
     * 新增/修改品类下的属性排序
     *
     * @param sysUser
     * @param categoryAttributeSortRequestList
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveCategoryAttributeSort(UserLoginCacheDTO sysUser, List<CategoryAttributeSortRequest> categoryAttributeSortRequestList) {
        List<ProductCategoryAttribute> productCategoryAttributeList = categoryAttributeSortRequestList
                .stream()
                .map(item -> {
                    ProductCategoryAttribute productCategoryAttribute = new ProductCategoryAttribute();
                    BeanUtil.copyProperties(item, productCategoryAttribute);
                    return productCategoryAttribute;
                }).collect(Collectors.toList());
        productCategoryAttributeList.forEach(productCategoryAttribute -> {
            productCategoryAttributeRepository.updateData(productCategoryAttribute.getCategoryId(), productCategoryAttribute.getAttributeId(), productCategoryAttribute.getSort());
        });
        return true;
    }

}

